package cn.lili.modules.serviceimpl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.SceneEnums;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.SnowFlake;
import cn.lili.common.utils.StringUtils;
import cn.lili.modules.mapper.OperationCenterWithdrawalMapper;
import cn.lili.modules.member.client.UserClient;
import cn.lili.modules.member.entity.dos.User;
import cn.lili.modules.operationcenter.entity.dos.OperationCenter;
import cn.lili.modules.operationcenter.entity.dos.OperationCenterWithdrawal;
import cn.lili.modules.operationcenter.entity.dto.OperationCenterWithdrawalDTO;
import cn.lili.modules.operationcenter.entity.enums.OperationCenterStatusEnum;
import cn.lili.modules.payment.entity.enums.WithdrawStatusEnum;
import cn.lili.modules.promotion.entity.enums.GreenAmountInfoStatusEnum;
import cn.lili.modules.promotion.entity.enums.GreenProjectStatusEnum;
import cn.lili.modules.service.OperationCenterService;
import cn.lili.modules.service.OperationCenterWithdrawalService;
import com.api.PayApi;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pojos.UserRes;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Service
@RequiredArgsConstructor
@Slf4j
public class OperationCenterWithdrawalServiceImpl extends ServiceImpl<OperationCenterWithdrawalMapper, OperationCenterWithdrawal>
        implements OperationCenterWithdrawalService {

    private final OperationCenterService operationCenterService;

    @Resource
    private RedissonClient redisson;

    private final PayApi payApi;

    private final UserClient userClient;

    /**
     * 申请提现
     * @param money
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean greenWithdrawal (String money) {
        if (!SceneEnums.OPERATION_CENTER.name().equals(Objects.requireNonNull(UserContext.getCurrentUser()).getScene().name())) {
            throw new ServiceException("权限不足");
        }

        OperationCenter operationCenter = operationCenterService.getOperationCenter(Objects.requireNonNull(UserContext.getCurrentUser()).getId());
        if (null == operationCenter || !operationCenter.getStatus().equals(OperationCenterStatusEnum.OPEN.name())) {
            throw new ServiceException("账号数据异常");
        }

        if (StringUtils.isEmpty(operationCenter.getSettlementBankAccountNumber())
                || StringUtils.isEmpty(operationCenter.getSettlementBankName())
                || StringUtils.isEmpty(operationCenter.getSettlementBankAccountName())) {
            throw new ServiceException("您的提现账户信息不完善，请补充");
        }

        OperationCenterWithdrawal todayWithdrawal = getTodayWithdrawal(operationCenter.getId());
        if (null != todayWithdrawal) {
            throw new ServiceException("操作失败，每天只能提交一次提现申请");
        }

        UserRes userRes = payApi.getUserMoney(Objects.requireNonNull(UserContext.getCurrentUser()).getOpenId());
        if (null == userRes || null == userRes.getGmoney()) {
            throw new ServiceException("账户异常");
        }

        String withdrawalMoney = new BigDecimal(money).multiply(new BigDecimal("100")).stripTrailingZeros().toPlainString();

        if (Double.parseDouble(userRes.getGmoney()) < Double.parseDouble(withdrawalMoney)) {
            throw new ServiceException("申请提现金额大于账户余额");
        }

        OperationCenterWithdrawal withdrawal = new OperationCenterWithdrawal();
        withdrawal.setOperationId(operationCenter.getId());
        withdrawal.setOperationName(operationCenter.getOperationName());
        withdrawal.setMoney(Double.valueOf(money));
        withdrawal.setUserId(operationCenter.getUserId());
        withdrawal.setOrderNo(SnowFlake.createStr("WD"));
        withdrawal.setStatus(WithdrawStatusEnum.APPLY.name());
        withdrawal.setGreenScoreType(GreenAmountInfoStatusEnum.DISBURSE.name());
        withdrawal.setRemarks(operationCenter.getOperationName() + " 提现至 "  + operationCenter.getSettlementBankAccountNumber());
        Double beforeMoney = CurrencyUtil.div(Double.parseDouble(userRes.getGmoney()), 100);
        Double afterMoney = CurrencyUtil.div(
                CurrencyUtil.sub(Double.parseDouble(userRes.getGmoney()),
                        Double.parseDouble(String.valueOf(withdrawalMoney)))
                , 100);
        withdrawal.setBeforeMoney(beforeMoney);
        withdrawal.setAfterMoney(afterMoney);
        withdrawal.setSettlementBankName(operationCenter.getSettlementBankName());
        withdrawal.setSettlementBankAccountName(operationCenter.getSettlementBankAccountName());
        withdrawal.setSettlementBankAccountNumber(operationCenter.getSettlementBankAccountNumber());

        try {
            if (this.save(withdrawal)) {
                boolean payment = payApi.strikeBalance(Objects.requireNonNull(UserContext.getCurrentUser()).getOpenId(), operationCenter.getOperationName() + "提现，金额：" + money, Integer.parseInt(withdrawalMoney), 2, 3, "GMONEY");
                if (!payment) {
                    throw new ServiceException("用户提现失败");
                }
            }
        }catch (Exception e) {
            log.error("提现失败：{}", e);
            throw new ServiceException("服务异常，提现失败");
        }
        return true;
    }

    /**
     * 提现审核
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean applyWithdrawal (OperationCenterWithdrawal withdrawal) {
        if (StringUtils.isEmpty(withdrawal.getId())
                || StringUtils.isEmpty(withdrawal.getStatus())) {
            throw new ServiceException("参数错误");
        }

        if (!SceneEnums.MANAGER.name().equals(Objects.requireNonNull(UserContext.getCurrentUser()).getScene().name())) {
            throw new ServiceException("权限不足");
        }

        RLock rLock = redisson.getLock("applyWithdrawal_" + withdrawal.getId());
        try {
            rLock.lock();
            OperationCenterWithdrawal operationCenterWithdrawal = this.getById(withdrawal.getId());
            if (null == operationCenterWithdrawal) {
                throw new ServiceException("数据不存在");
            }

            if (operationCenterWithdrawal.getStatus().equals(withdrawal.getStatus())) {
                throw new ServiceException("该数据已审核");
            }

            if (withdrawal.getStatus().equals(WithdrawStatusEnum.FAIL_AUDITING.name())) {
                User user = userClient.getById(operationCenterWithdrawal.getUserId());
                if (null == user) {
                    throw new ServiceException("用户数据异常");
                }

                BigInteger money = BigDecimal.valueOf(operationCenterWithdrawal.getMoney()).multiply(new BigDecimal(100)).toBigInteger();
                boolean flag = payApi.paymentV3(user.getOpenId(), "提现审核未通过，退回提现金额", money.intValue(), 1, 104, "GMONEY", operationCenterWithdrawal.getOrderNo());
                if (!flag) {
                    throw new ServiceException("用户提现失败");
                }
            }
        }catch (Exception e) {
            log.info("#applyWithdrawal# ");
            throw e;
        }finally {
            rLock.unlock();
        }
        withdrawal.setAuditTime(new Date());
        return this.updateById(withdrawal);
    }

    /**
     * 导出提现申请记录
     * @param operationCenterWithdrawal
     * @return
     */
    @Override
    public List<OperationCenterWithdrawal> exportWithdrawal (OperationCenterWithdrawal operationCenterWithdrawal) {
        LambdaQueryWrapper<OperationCenterWithdrawal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotEmpty(operationCenterWithdrawal.getOperationId()), OperationCenterWithdrawal::getOperationId, operationCenterWithdrawal.getOperationId());
        queryWrapper.like(StringUtils.isNotEmpty(operationCenterWithdrawal.getOperationName()), OperationCenterWithdrawal::getOperationName, operationCenterWithdrawal.getOperationName());
        queryWrapper.eq(StringUtils.isNotEmpty(operationCenterWithdrawal.getStatus()), OperationCenterWithdrawal::getStatus, operationCenterWithdrawal.getStatus());
        queryWrapper.ge(null != operationCenterWithdrawal.getStartDate(), OperationCenterWithdrawal::getCreateTime, operationCenterWithdrawal.getStartDate());
        queryWrapper.ge(null != operationCenterWithdrawal.getEndDate(), OperationCenterWithdrawal::getCreateTime, operationCenterWithdrawal.getEndDate());
        return this.list(queryWrapper);
    }

    public OperationCenterWithdrawal getTodayWithdrawal (String operationId) {
        LocalDate today = LocalDate.now();
        LambdaQueryWrapper<OperationCenterWithdrawal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OperationCenterWithdrawal::getOperationId, operationId);
        queryWrapper.eq(OperationCenterWithdrawal::getStatus, WithdrawStatusEnum.APPLY.name());
        queryWrapper.between(OperationCenterWithdrawal::getCreateTime, today.atStartOfDay(), today.atTime(23, 59, 59));
        queryWrapper.last("limit 1");
        return this.getOne(queryWrapper);
    }


    /**
     * 分页获取绿色积分提现记录
     * @param dto
     * @return
     */
    @Override
    public Page<OperationCenterWithdrawal> selectOperationCenterWithdrawalPageList(OperationCenterWithdrawalDTO dto) {
        if (SceneEnums.OPERATION_CENTER.name().equals(Objects.requireNonNull(UserContext.getCurrentUser()).getScene().name())) {
            dto.setUserId(Objects.requireNonNull(UserContext.getCurrentUser()).getId());
        }else if (SceneEnums.MANAGER.name().equals(Objects.requireNonNull(UserContext.getCurrentUser()).getScene().name())) {
            dto.setType(GreenAmountInfoStatusEnum.DISBURSE.name());
        }
        //分页查询提现记录列表
        Page<OperationCenterWithdrawal> page = baseMapper.selectPage(new Page<>(dto.getPageNumber(), dto.getPageSize()), dto.toQueryWapper());
        if (CollUtil.isNotEmpty(page.getRecords())) {
            page.getRecords().stream().forEach(e -> {
                if (StrUtil.isNotEmpty(e.getStatus())) {
                    //取枚举值
                    e.setStatusValue(WithdrawStatusEnum.valueOf(e.getStatus()).description());
                }
            });
        }
        return page;
    }
}
